{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d4aa16f5-abc8-4ed3-8a71-54837fe46917",
      "metadata": {
        "id": "d4aa16f5-abc8-4ed3-8a71-54837fe46917"
      },
      "source": [
        "# Breakpoints\n",
        "\n",
        "## Review\n",
        "\n",
        "For `human-in-the-loop`, we often want to see our graph outputs as its running.\n",
        "\n",
        "We laid the foundations for this with streaming.\n",
        "\n",
        "## Goals\n",
        "\n",
        "Now, let's talk about the motivations for `human-in-the-loop`:\n",
        "\n",
        "(1) `Approval` - We can interrupt our agent, surface state to a user, and allow the user to accept an action\n",
        "\n",
        "(2) `Debugging` - We can rewind the graph to reproduce or avoid issues\n",
        "\n",
        "(3) `Editing` - You can modify the state\n",
        "\n",
        "LangGraph offers several ways to get or update agent state to support various `human-in-the-loop` workflows.\n",
        "\n",
        "First, we'll introduce [breakpoints](https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/breakpoints/#simple-usage), which provide a simple way to stop the graph at specific steps.\n",
        "\n",
        "We'll show how this enables user `approval`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "35842345-0694-4f0a-aa62-7d4898abf653",
      "metadata": {
        "id": "35842345-0694-4f0a-aa62-7d4898abf653"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install --quiet -U langgraph langchain_google_genai langgraph_sdk"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "67d91f7c",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "67d91f7c",
        "outputId": "72ea8fe5-2482-4a0e-f193-28e349c1d71a"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "env: GOOGLE_API_KEY=AIzaSyDojJx56O8nzFauBKc_rj_0o7wvAnQgHAI\n",
            "AIzaSyDojJx56O8nzFauBKc_rj_0o7wvAnQgHAI\n"
          ]
        }
      ],
      "source": [
        "from google.colab import userdata\n",
        "\n",
        "%env GOOGLE_API_KEY = {userdata.get('GEMINI_API_KEY')}\n",
        "\n",
        "import os\n",
        "print(os.environ[\"GOOGLE_API_KEY\"])"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "%env LANGCHAIN_API_KEY = {userdata.get('LANGCHAIN_API_KEY')}\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "HKrhR660NQWV",
        "outputId": "75ed1520-7ef5-4b49-8395-20b722e0c0eb"
      },
      "id": "HKrhR660NQWV",
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "env: LANGCHAIN_API_KEY=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\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "id": "31d8b4cd-e3ff-48cc-b7b2-f83fadb1c86b",
      "metadata": {
        "id": "31d8b4cd-e3ff-48cc-b7b2-f83fadb1c86b"
      },
      "source": [
        "## Breakpoints for human approval\n",
        "\n",
        "Let's re-consider the simple agent that we worked with in Module 1.\n",
        "\n",
        "Let's assume that are concerned about tool use: we want to approve the agent to use any of its tools.\n",
        "\n",
        "All we need to do is simply compile the graph with `interrupt_before=[\"tools\"]` where `tools` is our tools node.\n",
        "\n",
        "This means that the execution will be interrupted before the node `tools`, which executes the tool call."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "b94d1a90-2fe3-4b2a-a901-3bdb89e37edc",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "b94d1a90-2fe3-4b2a-a901-3bdb89e37edc",
        "outputId": "712f8368-dffb-405e-87c7-98543a2b312d"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n",
            "WARNING:langchain_google_genai._function_utils:Key 'title' is not supported in schema, ignoring\n"
          ]
        }
      ],
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "from langchain_core.tools import tool\n",
        "\n",
        "def multiply(a: int, b: int) -> int:\n",
        "    \"\"\"Multiply a and b.\n",
        "\n",
        "    Args:\n",
        "        a: first int\n",
        "        b: second int\n",
        "    \"\"\"\n",
        "    return a * b\n",
        "\n",
        "# This will be a tool\n",
        "def add(a: int, b: int) -> int:\n",
        "    \"\"\"Adds a and b.\n",
        "\n",
        "    Args:\n",
        "        a: first int\n",
        "        b: second int\n",
        "    \"\"\"\n",
        "    return a + b\n",
        "\n",
        "def divide(a: int, b: int) -> float:\n",
        "    \"\"\"Adds a and b.\n",
        "\n",
        "    Args:\n",
        "        a: first int\n",
        "        b: second int\n",
        "    \"\"\"\n",
        "    return a / b\n",
        "\n",
        "tools: list[tool] = [add, multiply, divide]\n",
        "llm: ChatGoogleGenerativeAI = ChatGoogleGenerativeAI(model = \"gemini-1.5-flash\")\n",
        "llm_with_tools = llm.bind_tools(tools)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "ac06feae-d12b-490b-95e7-38cf40b74202",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 308
        },
        "id": "ac06feae-d12b-490b-95e7-38cf40b74202",
        "outputId": "d4909cc3-9a9a-4ca2-fdfa-ffb2bd715042"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "from IPython.display import Image, display\n",
        "\n",
        "from langgraph.checkpoint.memory import MemorySaver\n",
        "from langgraph.graph import MessagesState\n",
        "from langgraph.graph import START, StateGraph\n",
        "from langgraph.prebuilt import tools_condition, ToolNode\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
        "\n",
        "# System message\n",
        "sys_msg = SystemMessage(content=\"You are a helpful assistant tasked with performing arithmetic on a set of inputs.\")\n",
        "\n",
        "# Node\n",
        "def assistant(state: MessagesState):\n",
        "   return {\"messages\": [llm_with_tools.invoke([sys_msg] + state[\"messages\"])]}\n",
        "\n",
        "# Graph\n",
        "builder: StateGraph = StateGraph(MessagesState)\n",
        "\n",
        "# Define nodes: these do the work\n",
        "builder.add_node(\"assistant\", assistant)\n",
        "builder.add_node(\"tools\", ToolNode(tools))\n",
        "\n",
        "# Define edges: these determine the control flow\n",
        "builder.add_edge(START, \"assistant\")\n",
        "builder.add_conditional_edges(\n",
        "    \"assistant\",\n",
        "    # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools\n",
        "    # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END\n",
        "    tools_condition,\n",
        ")\n",
        "builder.add_edge(\"tools\", \"assistant\")\n",
        "\n",
        "memory: MemorySaver = MemorySaver()\n",
        "graph: CompiledStateGraph = builder.compile(interrupt_before=[\"tools\"], checkpointer=memory)\n",
        "\n",
        "# Show\n",
        "display(Image(graph.get_graph(xray=True).draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "a783efac-46a9-4fb4-a1c6-a11b02540448",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "a783efac-46a9-4fb4-a1c6-a11b02540448",
        "outputId": "22b513aa-f6a0-4a5c-8b1c-f291e20532f2"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Multiply 2 and 3\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  multiply (4449d26c-16a6-4388-8e80-ccb57c46a884)\n",
            " Call ID: 4449d26c-16a6-4388-8e80-ccb57c46a884\n",
            "  Args:\n",
            "    b: 3.0\n",
            "    a: 2.0\n"
          ]
        }
      ],
      "source": [
        "# Input\n",
        "initial_input = {\"messages\": HumanMessage(content=\"Multiply 2 and 3\")}\n",
        "\n",
        "# Thread\n",
        "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Run the graph until the first interruption\n",
        "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
        "    event['messages'][-1].pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50d49669-b1a5-42c2-bdb8-052da89bd7c4",
      "metadata": {
        "id": "50d49669-b1a5-42c2-bdb8-052da89bd7c4"
      },
      "source": [
        "We can get the state and look at the next node to call.\n",
        "\n",
        "This is a nice way to see that the graph has been interrupted."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "61569596-8342-4a37-9c99-e3a9dccb18ee",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "61569596-8342-4a37-9c99-e3a9dccb18ee",
        "outputId": "ecf8a83e-938c-4ff2-a664-7391df136aa1"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "('tools',)"
            ]
          },
          "metadata": {},
          "execution_count": 13
        }
      ],
      "source": [
        "state = graph.get_state(thread)\n",
        "state.next"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2fea0fb5-3145-4f34-bcc0-9c9e8972d6b4",
      "metadata": {
        "id": "2fea0fb5-3145-4f34-bcc0-9c9e8972d6b4"
      },
      "source": [
        "Now, we'll introduce a nice trick.\n",
        "\n",
        "When we invoke the graph with `None`, it will just continue from the last state checkpoint!\n",
        "\n",
        "![breakpoints.jpg](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbae7985b747dfed67775d_breakpoints1.png)\n",
        "\n",
        "For clarity, LangGraph will re-emit the current state, which contains the `AIMessage` with tool call.\n",
        "\n",
        "And then it will proceed to execute the following steps in the graph, which start with the tool node.\n",
        "\n",
        "We see that the tool node is run with this tool call, and it's passed back to the chat model for our final answer."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "896a5f41-7386-4bfa-a78e-3e6ca5e26641",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "896a5f41-7386-4bfa-a78e-3e6ca5e26641",
        "outputId": "0b6bb08f-39cd-4f24-a962-c40e7c369cdf"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  multiply (4449d26c-16a6-4388-8e80-ccb57c46a884)\n",
            " Call ID: 4449d26c-16a6-4388-8e80-ccb57c46a884\n",
            "  Args:\n",
            "    b: 3.0\n",
            "    a: 2.0\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: multiply\n",
            "\n",
            "6\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "The result is 6.\n"
          ]
        }
      ],
      "source": [
        "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
        "    event['messages'][-1].pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "37f91a0c-7cc1-4437-adc7-b36abb29beb1",
      "metadata": {
        "id": "37f91a0c-7cc1-4437-adc7-b36abb29beb1"
      },
      "source": [
        "Now, lets bring these together with a specific user approval step that accepts user input."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "95a0eb50-66e3-4538-8103-207aae175154",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "95a0eb50-66e3-4538-8103-207aae175154",
        "outputId": "1b6c7cb3-4528-48c5-b64f-4bf73351824d"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "================================\u001b[1m Human Message \u001b[0m=================================\n",
            "\n",
            "Multiply 2 and 3\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  multiply (6c58670d-fcff-402c-a580-dae979d800fa)\n",
            " Call ID: 6c58670d-fcff-402c-a580-dae979d800fa\n",
            "  Args:\n",
            "    a: 2.0\n",
            "    b: 3.0\n",
            "Do you want to call the tool? (yes/no): yes\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "Tool Calls:\n",
            "  multiply (6c58670d-fcff-402c-a580-dae979d800fa)\n",
            " Call ID: 6c58670d-fcff-402c-a580-dae979d800fa\n",
            "  Args:\n",
            "    a: 2.0\n",
            "    b: 3.0\n",
            "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
            "Name: multiply\n",
            "\n",
            "6\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "The answer is 6.\n"
          ]
        }
      ],
      "source": [
        "# Input\n",
        "initial_input = {\"messages\": HumanMessage(content=\"Multiply 2 and 3\")}\n",
        "\n",
        "# Thread\n",
        "thread = {\"configurable\": {\"thread_id\": \"2\"}}\n",
        "\n",
        "# Run the graph until the first interruption\n",
        "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
        "    event['messages'][-1].pretty_print()\n",
        "\n",
        "# Get user feedback\n",
        "user_approval = input(\"Do you want to call the tool? (yes/no): \")\n",
        "\n",
        "# Check approval\n",
        "if user_approval.lower() == \"yes\":\n",
        "\n",
        "    # If approved, continue the graph execution\n",
        "    for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
        "        event['messages'][-1].pretty_print()\n",
        "\n",
        "else:\n",
        "    print(\"Operation cancelled by user.\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b8ff8762-6fa1-4373-954a-e7f479ee0efb",
      "metadata": {
        "id": "b8ff8762-6fa1-4373-954a-e7f479ee0efb"
      },
      "source": [
        "### Breakpoints with LangGraph API\n",
        "\n",
        "--\n",
        "\n",
        "**⚠️ DISCLAIMER**\n",
        "\n",
        "*Running Studio currently requires a Mac. If you are not using a Mac, then skip this step.*\n",
        "\n",
        "*Also, if you are running this notebook in CoLab, then skip this step.*\n",
        "\n",
        "--\n",
        "\n",
        "Let's load our `agent` in the Studio UI, which uses `module-3/studio/agent.py` set in `module-3/studio/langgraph.json`.\n",
        "\n",
        "Let's get the URL for the local deployment from Studio.\n",
        "\n",
        "![Screenshot 2024-08-26 at 9.36.41 AM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbae7989b1d60204c199dc_breakpoints2.png)\n",
        "\n",
        "The LangGraph API [supports breakpoints](https://langchain-ai.github.io/langgraph/cloud/how-tos/human_in_the_loop_breakpoint/#sdk-initialization)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "63c2eaf1-6b8b-4d80-9902-98ae5587bcf9",
      "metadata": {
        "id": "63c2eaf1-6b8b-4d80-9902-98ae5587bcf9"
      },
      "outputs": [],
      "source": [
        "import platform\n",
        "\n",
        "if platform.system() != 'Darwin':\n",
        "    raise Exception(\"Unfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "fb1dd890-c216-4802-9e33-b637e491e144",
      "metadata": {
        "id": "fb1dd890-c216-4802-9e33-b637e491e144"
      },
      "outputs": [],
      "source": [
        "from langgraph_sdk import get_client\n",
        "client = get_client(url=\"https://cardiff-rivers-timothy-supply.trycloudflare.com\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e80d969-d065-45d7-8bfc-a403a0a1079b",
      "metadata": {
        "id": "1e80d969-d065-45d7-8bfc-a403a0a1079b"
      },
      "source": [
        "As shown above, we can add `interrupt_before=[\"node\"]` when compiling the graph that is running in Studio.\n",
        "\n",
        "However, with the API, you can also pass `interrupt_before` to the stream method directly."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "de9c5017-3a15-46f6-8edf-3997613da323",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "de9c5017-3a15-46f6-8edf-3997613da323",
        "outputId": "4b820a5e-25da-400e-fa2d-a2549450c484"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Receiving new event of type: metadata...\n",
            "--------------------------------------------------\n",
            "Receiving new event of type: values...\n",
            "{'content': 'Multiply 2 and 3', 'additional_kwargs': {'example': False, 'additional_kwargs': {}, 'response_metadata': {}}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '53955f1f-1d43-4049-9662-89f99a0f64e0', 'example': False}\n",
            "--------------------------------------------------\n",
            "Receiving new event of type: values...\n",
            "{'content': '', 'additional_kwargs': {'function_call': {'name': 'multiply', 'arguments': '{\"a\": 2.0, \"b\": 3.0}'}}, 'response_metadata': {'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, 'type': 'ai', 'name': None, 'id': 'run-77d3d27e-39bd-4399-b2b1-5b4840c4ae38-0', 'example': False, 'tool_calls': [{'name': 'multiply', 'args': {'a': 2.0, 'b': 3.0}, 'id': 'e187fd16-f944-4c07-85f4-f7d879ac3828', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 210, 'output_tokens': 18, 'total_tokens': 228}}\n",
            "--------------------------------------------------\n"
          ]
        }
      ],
      "source": [
        "initial_input = {\"messages\": HumanMessage(content=\"Multiply 2 and 3\")}\n",
        "thread = await client.threads.create()\n",
        "async for chunk in client.runs.stream(\n",
        "    thread[\"thread_id\"],\n",
        "    assistant_id=\"agent\",\n",
        "    input=initial_input,\n",
        "    stream_mode=\"values\",\n",
        "    interrupt_before=[\"tools\"],\n",
        "):\n",
        "    print(f\"Receiving new event of type: {chunk.event}...\")\n",
        "    messages = chunk.data.get('messages', [])\n",
        "    if messages:\n",
        "        print(messages[-1])\n",
        "    print(\"-\" * 50)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b64272d1-c6ee-435f-9890-9b6c3525ca6c",
      "metadata": {
        "id": "b64272d1-c6ee-435f-9890-9b6c3525ca6c"
      },
      "source": [
        "Now, we can proceed from the breakpoint just like we did before by passing the `thread_id` and `None` as the input!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "76284730-9c90-46c4-8295-400a49760b07",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "76284730-9c90-46c4-8295-400a49760b07",
        "outputId": "16ea3896-2c64-4ed2-991a-4528323ebe44"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Receiving new event of type: metadata...\n",
            "--------------------------------------------------\n",
            "Receiving new event of type: values...\n",
            "{'content': '', 'additional_kwargs': {'function_call': {'name': 'multiply', 'arguments': '{\"a\": 2.0, \"b\": 3.0}'}}, 'response_metadata': {'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, 'type': 'ai', 'name': None, 'id': 'run-77d3d27e-39bd-4399-b2b1-5b4840c4ae38-0', 'example': False, 'tool_calls': [{'name': 'multiply', 'args': {'a': 2.0, 'b': 3.0}, 'id': 'e187fd16-f944-4c07-85f4-f7d879ac3828', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 210, 'output_tokens': 18, 'total_tokens': 228}}\n",
            "--------------------------------------------------\n",
            "Receiving new event of type: values...\n",
            "{'content': '6', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'multiply', 'id': 'fc779fda-c55e-43f1-9ba7-3bff56eb16c5', 'tool_call_id': 'e187fd16-f944-4c07-85f4-f7d879ac3828', 'artifact': None, 'status': 'success'}\n",
            "--------------------------------------------------\n",
            "Receiving new event of type: values...\n",
            "{'content': 'The answer is 6. \\n', 'additional_kwargs': {}, 'response_metadata': {'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, 'type': 'ai', 'name': None, 'id': 'run-8d479dfb-305d-4196-b19f-32d1135a4553-0', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': {'input_tokens': 243, 'output_tokens': 6, 'total_tokens': 249}}\n",
            "--------------------------------------------------\n"
          ]
        }
      ],
      "source": [
        "async for chunk in client.runs.stream(\n",
        "    thread[\"thread_id\"],\n",
        "    \"agent\",\n",
        "    input=None,\n",
        "    stream_mode=\"values\",\n",
        "    interrupt_before=[\"tools\"],\n",
        "):\n",
        "    print(f\"Receiving new event of type: {chunk.event}...\")\n",
        "    messages = chunk.data.get('messages', [])\n",
        "    if messages:\n",
        "        print(messages[-1])\n",
        "    print(\"-\" * 50)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4575970f-42e2-4d03-b18a-aacaa8233b53",
      "metadata": {
        "id": "4575970f-42e2-4d03-b18a-aacaa8233b53"
      },
      "source": []
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "Sokc0EGjR33U"
      },
      "id": "Sokc0EGjR33U",
      "execution_count": null,
      "outputs": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.4"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}